package main

import "fmt"

/*
区别于C/C++中的指针，Go语言中的指针不能进行偏移和运算，是安全指针。要搞明白Go语言中的指针需要先知道3个概念：指针地址、指针类型和指针取值。
任何程序数据载入内存后，在内存都有他们的地址，这就是指针。而为了保存一个数据在内存中的地址，我们就需要指针变量。

记住两个符号：&（取地址）和*（根据地址取值）

总结： 取地址操作符&和取值操作符*是一对互补操作符，&取出地址，*根据地址取出地址指向的值。

变量、指针地址、指针变量、取地址、取值的相互关系和特性如下：
对变量进行取地址（&）操作，可以获得这个变量的指针变量。
指针变量的值是指针地址。
对指针变量进行取值（*）操作，可以获得指针变量指向的原变量的值。

在Go语言中对于引用类型的变量，我们在使用的时候不仅要声明它，还要为它分配内存空间，否则我们的值就没办法存储。
而对于值类型的声明不需要分配内存空间，是因为它们在声明的时候已经默认分配好了内存空间。要分配内存，就引出来今天的new和make。
Go语言中new和make是内建的两个函数，主要用来分配内存。

new函数不太常用，使用new函数得到的是一个类型的指针，并且该指针对应的值为该类型的零值。
make也是用于内存分配的，区别于new，它只用于slice、map以及chan的内存创建，而且它返回的类型就是这三个类型本身，而不是他们的指针类型，
因为这三种类型就是引用类型，所以就没有必要返回他们的指针了。

new与make的区别
1. 二者都是用来做内存分配的。
2. make只用于slice、map以及channel的初始化，返回的还是这三个引用类型本身；
3. 而new用于类型的内存分配，并且内存对应的值为类型零值，返回的是指向类型的指针。


*/

var str = "永远不要高估自己"
var straddr = &str

func main() {
	// 指针地址和指针类型
	fmt.Printf("str变量：%T %v \n", str, str)
	fmt.Printf("str变量地址：%T %v \n", &str, &str)

	fmt.Printf("straddr变量：%T %v \n", straddr, straddr)
	fmt.Printf("straddr变量地址：%T %v \n", &straddr, &straddr)

	//取指针的值
	fmt.Printf("straddr指针的值：%v \n", *straddr)

	// var a *int只是声明了一个指针变量a但是没有初始化，指针作为引用类型需要初始化后才会拥有内存空间，才可以给它赋值。
	var a *int
	fmt.Printf("a: %T %v \n", a, a)
	// new分配内存
	a = new(int)
	fmt.Printf("a: %T %v \n", a, a)
	*a = 100
	fmt.Printf("a: %T %v   *a: %v \n", a, a, *a)

	var b map[string]int
	// make分配内存
	b = make(map[string]int, 10)
	b["beijing"] = 100101
	fmt.Println(b)

	var c []string
	c = make([]string, 4, 5) // 容量必须比长度大
	c[0] = "beijing"
	c[2] = "tianjin"
	c[1] = "shanghai"
	c[3] = "guangzhou"
	fmt.Println(c)

}
